package com.pro.utils;

import com.pro.pojo.JsComment;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.*;

/**
 * ClassName:SensitiveWordsUtil
 * Package:com.pro.utils
 * Description:
 *
 * @Date:2021/11/15 16:33
 * @Author: LBF
 */
//敏感词过滤
public class SensitiveWordsUtil {
    //敏感词存放地址
    public static String filePath = System.getProperty("user.dir")+"/src/main//resources/static/sensitive-words/network-sensitive-words.txt";//敏感词库文件路径
    public static Set<String> words;
    public static Map<String,String> wordMap;
    public static int minMatchTYpe = 1;      //最小匹配规则
    public static int maxMatchType = 2;      //最大匹配规则

    static{
        /*SensitiveWordsUtil.words  获取set集合
         * readTxtByLine(filePath)  将敏感词库地址传入方法中通过readTxtByLine方法将敏感词库中的词汇取出存放进set集合中
         * addBadWordToHashMap(SensitiveWordsUtil.words)； 调用addBadWordToHashMap方法将敏感词库中的数据化为树
         * */
        SensitiveWordsUtil.words = readTxtByLine(filePath);
        addBadWordToHashMap(SensitiveWordsUtil.words);
    }

    public static Set<String> readTxtByLine(String path){
        Set<String> keyWordSet = new HashSet<String>();
        File file=new File(path);
        if(!file.exists()){      //文件流是否存在
            return keyWordSet;
        }
        BufferedReader reader=null;
        String temp=null;
        //int line=1;
        try{
            //reader=new BufferedReader(new FileReader(file));这样在web运行的时候，读取会乱码
            reader=new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
            while((temp=reader.readLine())!=null){
                //System.out.println("line"+line+":"+temp);
                keyWordSet.add(temp);
                //line++;
            }
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            if(reader!=null){
                try{
                    reader.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
        return keyWordSet;
    }

    /**
     * TODO 将我们的敏感词库构建成了一个类似与一颗一颗的树，这样我们判断一个词是否为敏感词时就大大减少了检索的匹配范围。
     * @param keyWordSet 敏感词库
     * @author yqwang0907
     * @date 2018年2月28日下午5:28:08
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static void addBadWordToHashMap(Set<String> keyWordSet) {
        //keyWordSet.size() 获取set内容数量将数量值定义为HashMap集合的长度 防止过多的扩容造成资源浪费
        wordMap = new HashMap(keyWordSet.size());     //初始化敏感词容器，减少扩容操作
        String key = null;
        Map nowMap = null;
        Map<String, String> newWorMap = null;
        //迭代keyWordSet
        Iterator<String> iterator = keyWordSet.iterator();//keyWordSer.iterator()通过迭代器对set集合进行遍历
        //通过while循环将迭代出来的值进行遍历
        while(iterator.hasNext()){
            key = iterator.next();//key为关键字
            nowMap = wordMap;//nowMap变为空的HashMap
            //此时key为一个字符串由于字符串其实为一个char数组，通过数组长度来对字符串进行遍历
            for(int i = 0 ; i < key.length() ; i++){
                //将下标为i的字符存为char类型
                char keyChar = key.charAt(i);//转换成char型
                //nowMap.get(keyChar)判断nowMap集合中是否存在keyChar这个子符
                Object wordMap = nowMap.get(keyChar);//获取
                if(wordMap != null){     //如果存在该key，直接将该字符存入到HashMap集合中
                    nowMap = (Map) wordMap;
                }
                else{     //不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                    newWorMap = new HashMap<String,String>();
                    newWorMap.put("isEnd", "0");   //不是最后一个
                    nowMap.put(keyChar, newWorMap);//将此keyChar做为一个节点
                    nowMap = newWorMap;
                }
                //如果当前字符的下标 和字符串总长度-1 一致则为最后一个
                if(i == key.length() - 1){
                    nowMap.put("isEnd", "1");    //最后一个
                }
            }
        }
    }

    /**
     * 检查文字中是否包含敏感字符，检查规则如下：<br>
     * @param txt
     * @param beginIndex
     * @param matchType
     * @return，如果存在，则返回敏感词字符的长度，不存在返回0
     * @version 1.0
     */
    @SuppressWarnings({ "rawtypes"})
    public static int checkBadWord(String txt,int beginIndex,int matchType){
        boolean  flag = false;    //敏感词结束标识位：用于敏感词只有1位的情况
        int matchFlag = 0;     //匹配标识数默认为0
        char word = 0;
        Map nowMap = wordMap; //变为长度为树节点数量总和的HashMap集合
        //beginIndex为当前遍历字符串中字符的下标 ,tet为当前传入的字符串
        for(int i = beginIndex; i < txt.length() ; i++){
            //获取当前字符串下标i的字符
            word = txt.charAt(i);
            //判断集合中是否有该字符
            nowMap = (Map) nowMap.get(word);     //获取指定key
            if(nowMap != null){     //存在，则判断是否为最后一个
                matchFlag++;     //找到相应key，匹配标识+1 ，matchFlag为返回的字符串长度+1
                if("1".equals(nowMap.get("isEnd"))){//如果为最后一个匹配规则,结束循环，返回匹配标识数
                    flag = true;       //结束标志位为true
                    if(minMatchTYpe == matchType){    //最小规则，直接返回,最大规则还需继续查找
                        break;
                    }
                }
            }
            else{     //不存在，直接返回
                break;
            }
        }
        /*“粉饰”匹配词库：“粉饰太平”竟然说是敏感词
         * “个人”匹配词库：“个人崇拜”竟然说是敏感词
         * if(matchFlag < 2 && !flag){
            matchFlag = 0;
        }*/
        //如果存在返回敏感词长度不存在直接返回0
        if(!flag){
            matchFlag = 0;
        }
        return matchFlag;
    }

    /**
     * 判断文字是否包含敏感字符
     * @param txt  文字
     * @param matchType  匹配规则 1：最小匹配规则，2：最大匹配规则
     * @return 若包含返回true，否则返回false
     * @version 1.0
     */
    public static boolean isContaintBadWord(String txt,int matchType){
        boolean flag = false;
        for(int i = 0 ; i < txt.length() ; i++){
            int matchFlag = checkBadWord(txt, i, matchType); //判断是否包含敏感字符
            if(matchFlag > 0){    //大于0存在，返回true
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 替换敏感字字符
     * @param txt
     * @param matchType
     * @param replaceChar 替换字符，默认*
     * @version 1.0
     */
    public static String replaceBadWord(String txt,int matchType,String replaceChar){
        String resultTxt = txt;
        //返回所有敏感字
        Set<String> set = getBadWord(txt, matchType);     //获取所有的敏感词
        //对敏感词进行迭代
        Iterator<String> iterator = set.iterator();
        String word = null;
        String replaceString = null;
        while (iterator.hasNext()) {
            //将当前字符赋值
            word = iterator.next();
            replaceString = getReplaceChars(replaceChar, word.length());
            //将星星与当前敏感词替换
            resultTxt = resultTxt.replaceAll(word, replaceString);
        }

        return resultTxt;
    }
    /**
     * 获取文字中的敏感词
     * @param txt 文字
     * @param matchType 匹配规则 1：最小匹配规则，2：最大匹配规则
     * @return
     * @version 1.0
     */
    public static Set<String> getBadWord(String txt , int matchType){
        Set<String> sensitiveWordList = new HashSet<String>();

        for(int i = 0 ; i < txt.length() ; i++){
            //获取长度
            int length = checkBadWord(txt, i, matchType);    //判断是否包含敏感字符
            //长度大于0代表存在敏感词
            if(length > 0){    //存在,加入list中
                //将当前位置敏感字切割出来并存入集合中
                sensitiveWordList.add(txt.substring(i, i+length));
                i = i + length - 1;    //减1的原因，是因为for会自增
            }
        }

        return sensitiveWordList;
    }

    /**
     * 获取替换字符串
     * @param replaceChar
     * @param length
     * @return
     * @version 1.0
     */
    private static String getReplaceChars(String replaceChar,int length){
        //获取传入的敏感词
        String resultReplace = replaceChar;
        //通过敏感词长度来决定追加多少星星
        for(int i = 1 ; i < length ; i++){
            resultReplace += replaceChar;
        }
        return resultReplace;
    }




    //进行单个评论内容的过滤
    public static String getFilter(String txt){
        return replaceBadWord(txt, 2, "*");
    }

    //进行评论集合的内容过滤
    public static List<JsComment> getListFilter(List<JsComment> comments){
        for (JsComment comment : comments) {
            comment.setContent(getFilter(comment.getContent()));
        }
        return comments;
    }
}
